package edu.washington.ling.syntric.io;

import java.util.LinkedList;

import edu.washington.ling.syntric.struct.tree.Node;
import edu.washington.ling.syntric.struct.tree.Token;
import edu.washington.ling.syntric.struct.tree.Tree;
import edu.washington.ling.syntric.util.LaTeXUtils;
import edu.washington.ling.syntric.util.Utils;


public class TreeWriterLaTeX extends TreeWriter {

	private String getNodeLine(Node n) {
		String label = n.getLabel();
		if (n.hasFeature("index")) {
			String index = n.getValue("index");
			label = String.format("%s_%s", label, index);
		}
		if (n.hasHeadWords()) {
			String headWordStr = Utils.join(n.getHeadWords(), "|");
			label = String.format("%s{\\scriptsize (%s)}", label, headWordStr);
		}
		return String.format("[.\\node{%s};", label);
	}

	private String getLeafTokenLine(Token t) {
		String tokName = "default";
		if(t.hasName()) {
			tokName = LaTeXUtils.formatForID(t.getName());
		}
		String label = LaTeXUtils.formatForLabel(t.getWord());
		if (t.hasFeature("ectype")) {
			label = t.getValue("ectype");
		}
		if(t.hasFeature("tindex")) {
			String tindex = t.getValue("tindex");
			label = String.format("%s^{%s}", label, tindex);
		}
		if(t.hasFeature("index")) {
			String index = t.getValue("index");
			label = String.format("%s_{%s}", label, index);
		}
		return String.format("\\node[token](%s){%s};", tokName, label);
	}
	private String getListTokenLine(Token tok, Token lastTok, String layer) {
		String line;
		String word = LaTeXUtils.formatForLabel(tok.getWord());
		if (tok.hasFeature("ectype")) {
			word = tok.getValue("ectype");
		}
		if(tok.hasFeature("tindex")) {
			String tindex = tok.getValue("tindex");
			word = String.format("%s^{%s}", word, tindex);
		}
		if(tok.hasFeature("index")) {
			String index = tok.getValue("index");
			word = String.format("%s_{%s}", word, index);
		}
		String tag = tok.getTag();
		if (layer.equals("DS-Const")) {
			if (tok.hasFeature("Ext-POS")) {
				tag = tok.getValue("Ext-POS");
			}
		}
		String label = String.format("%s\\nodepart{second}%s", word, tag);
		String tokName = LaTeXUtils.formatForID(tok.getName());
		if (lastTok != null) {
			String lastTokName = LaTeXUtils.formatForID(lastTok.getName());
			line = String.format("\\node[token,right=of %s](%s){%s};", lastTokName, tokName, label);
		}
		else {
			line = String.format("\\node[token](%s){%s};", tokName, label); 
		}
		return line;
	}
	
	private String getDSLinkLine(Token t, Tree tree) {
		Token head = t.getDSHead();
		String relation = t.getDSRelation();
		if (t.isScrambledLongDistance() || t.isScrambledOverArgument() || t.isScrambledOverModifier()) {
			relation = relation + " [scr]";
		}
		if (t.isExtraposed()) {
			relation = relation + " [extr]";
		}
		return getLinkLine(t, head, relation, tree);
	}
	private String getPBLinkLine(Token t, Token head, Tree tree) {
		String relation = t.getPBArgumentType(head);
		return getLinkLine(t, head, relation, tree);
	}
	private String getLinkLine(Token t, Token head, String relation, Tree tree) {
		String name = LaTeXUtils.formatForID(t.getName());
		String headName = LaTeXUtils.formatForID(head.getName());
		relation = LaTeXUtils.formatForLabel(relation);
		int headDist = tree.getTokenPosition(t) - tree.getTokenPosition(head);
		String bend;
		if (headDist < 0) {
			bend = "right";
		}
		else {
			bend = "left";
		}
		int bendDegree;
		double pos;
		switch (Math.abs(headDist)) {
			case 1:
				bendDegree = 32;
				pos = 0.5; break;
			case 2:
				bendDegree = 40;
				pos = 0.40; break;
			case 3:
				bendDegree = 48;
				pos = 0.30; break;
			case 4:
				bendDegree = 56; 
				pos = 0.25; break;
			case 5:
				bendDegree = 64; 
				pos = 0.20; break;
			case 6:
				bendDegree = 72;
				pos = 0.16; break;
			case 7:
				bendDegree = 80;
				pos = 0.12; break;
			default:
				bendDegree = 88;
				pos = 0.08; break;
		}
		return String.format("\\path[link] (%s.south) edge[bend %s=%d] node[pos=%.2f, below=-0.05, sloped] {\\scriptsize %s} (%s);", name, bend, bendDegree, pos, relation, headName);
	}
	private LinkedList<String> getDSRootLines(Token tok) {
		LinkedList<String> lines = new LinkedList<String>();
		String tokName = LaTeXUtils.formatForID(tok.getName());
		lines.add(String.format("\\node[below=of %s](root){\\bf root};", tokName));
		lines.add(String.format("\\path[link] (%s) edge (root);", tokName));
		return lines;
	}
	private LinkedList<String> getPSTreeLines(Node n){
		LinkedList<String> lines = new LinkedList<String>();
		if (n instanceof Token) {
			lines.add(getLeafTokenLine((Token) n));
		}
		else {
			lines.add(getNodeLine(n));
			for (Node child: n.getPSChildren()) {
				lines.addAll(getPSTreeLines(child));
			}
			String endBranchLine = "]";
			lines.add(endBranchLine);
		}
		return lines;
	}
	private LinkedList<String> getTokenLines(Tree tree) {
		Token lastTok = null;
		LinkedList<String> lines = new LinkedList<String>();
		for (Token t: tree.getTokenList()) {
			lines.add(getListTokenLine(t, lastTok, tree.getLayer()));
			lastTok = t;
		}
		return lines;
	}
	private LinkedList<String> getDSLinkLines(Tree tree){
		LinkedList<String> lines = new LinkedList<String>();
		for (Token t: tree.getTokenList()) {
			if (t.hasDSHead()) {
				lines.add(getDSLinkLine(t, tree));
			}
			else if (t.hasName()) {
				lines.addAll(getDSRootLines(t));
			}
		}
		return lines;
	}
	private LinkedList<String> getPBLinkLines(Tree tree){
		LinkedList<String> lines = new LinkedList<String>();
		for (Token t: tree.getTokenList()) {
			for (Token head: t.getPBHeads()) {
				lines.add(getPBLinkLine(t, head, tree));
			}
		}
		return lines;
	}
	private LinkedList<String> getTreeLines(Tree tree) {
		LinkedList<String> lines;
		if (tree.hasPSRoot()) {
			lines = getPSTreeLines(tree.getPSRoot());
			lines.addFirst("\\Tree");
		} else {
			lines = getTokenLines(tree);
		}
		if (tree.getLayer().startsWith("DS")) {
			lines.addAll(getDSLinkLines(tree));
		} 
		else if (tree.getLayer().startsWith("PB")) {
			lines.addAll(getPBLinkLines(tree));
		}
		if (lines.size() > 0) {
			if (tree.getLayer().equals("PS")) {
				lines.addFirst("\\begin{tikzpicture}[sibling distance=24pt, level distance=24pt]");
			} 
			else {
				lines.addFirst("\\begin{tikzpicture}[sibling distance=48pt, level distance=24pt]");
			}
			lines.addLast("\\end{tikzpicture}");
		}
		return lines;
	}
	
	@Override
	public TreeRepresentation writeTree(Tree tree) {
		LinkedList<String> lines = getTreeLines(tree);
		return new TreeRepresentation(lines, tree.getLayer(), "latex");
	}

}
